<!--
 * @Description: ------------ fileDescription -----------
 * @Author: snows_l snows_l@163.com
 * @Date: 2024-08-09 16:21:21
 * @LastEditors: snows_l snows_l@163.com
 * @LastEditTime: 2025-01-17 19:44:49
 * @FilePath: /BLOG/src/views/play/music/index.vue
-->
<template>
  <div class="music-warp">
    <PageTopCover
      :moduleTitle="'音乐'"
      :icon="'icon-a-Sheetmusic'"
      :textIcon="'🎧'"
      :coverImg="getOnlineImgUrl('/imgs/bg/93.png')"
      :isMobile="isMobi"
      :mudulDesc="'音乐是一种艺术形式，它是由音符组成的乐曲，旋律、节奏、和音色的组合，是人类创造的声音。'"
      :isArticle="false"></PageTopCover>
    <div class="music-list-content-warp" :class="{ 'm-music-list-content-warp': isMobi }">
      <div class="music-list-content" v-if="state.list.length > 0" :class="{ 'm-music-list-content': isMobi }">
        <div class="music-list">
          <div
            @click="handlePlay(item)"
            :id="'music-' + item.id"
            :class="{ active: state.musicId == item.id }"
            class="music-item pointer kbn-music intersection-observer hover-shadow"
            :data-tip="item.title"
            v-for="(item, index) in state.list"
            :key="index">
            <div class="music-item-warp">
              <div class="cover-warp" @click="e => e.stopPropagation()">
                <LImg class="cover-img" :src="item.img" alt="" />
              </div>
              <div class="music-info">
                <div class="info-item text music-title">{{ item.title }}</div>
                <div class="info-item text music-artist">ID：{{ item.id }}</div>
                <div class="info-item text music-artist">歌手：{{ item.artist }}</div>
                <div class="info-item text music-type">类型：{{ state.playList.find(row => row.value == item.type)?.label }}</div>
              </div>
            </div>
            <div class="is-playing-current">
              <span class="current-music" v-show="state.currentMusicId == item.id">☑️</span>
              <img v-show="state.isPlaying && state.currentMusicId == item.id" :src="getOnlineImgUrl('/imgs/common/playing.gif')" />
              <div
                v-loading="item.loading"
                :style="{ display: isMobi || item.loading ? 'block' : 'none', marginLeft: '10px' }"
                class="download-btn"
                @click="
                  e => {
                    e.stopPropagation();
                    hendleDownload(item, index);
                  }
                ">
                下载
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="no-article" :class="{ 'm-no-article': isMobi }" v-else>
        <Empty :text="'暂无音乐数据，期待您的分享~'" :loadingText="'音乐正在拼命加载中...'" :loading="state.loading" />
      </div>

      <LoadingMore
        v-if="store.projectConfig.pageType == 'more'"
        :loading="state.loading"
        :hasMore="state.isMore"
        :length="state.list.length"
        @loadMore="handleLoadMore"></LoadingMore>
      <CustomPage v-else :page="state.page" @change="handlePageChange" :loading="state.loading" scorllTop="music-list"></CustomPage>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { getDict } from '@/api/common';
import { getMusicList } from '@/api/music';
import $bus from '@/bus/index';
import useResize from '@/hooks/useResize';
import { useAppStore } from '@/store/app';
import { getOnlineImgUrl } from '@/utils/common';
import { onMounted, onUnmounted, onUpdated, reactive } from 'vue';
import { useRoute } from 'vue-router';

const route = useRoute();
const { isMobi } = useResize();

import useIntersectionObserver from '@/hooks/useIntersectionObserver';
useIntersectionObserver();
const store = useAppStore();

const state = reactive({
  musicId: route.query.id || '', // 根据id找到当前音乐
  loading: false,
  isPlaying: localStorage.getItem('isPlaying') == 'true' ? true : false,
  currentMusicId: localStorage.getItem('currentMusicId') || 0, // 当前播放音乐id
  list: [],
  allList: [],
  playList: [],
  updateTimer: 0,
  isMore: true,
  page: {
    page: 1,
    size: 30,
    total: 0
  }
});

const getPlayList = () => {
  getDict({ dictType: 'music_type' }).then(res => {
    state.playList = res.data;
    store.SET_MUSIC_DICT(res.data);
  });
};

// 获取音乐类型和文章类型
store.musicDict.length > 0 ? (state.playList = store.musicDict) : getPlayList();

const getMusicListFn = scorllTopCallback => {
  state.loading = true;
  getMusicList({ ...state.page })
    .then(res => {
      if (store.projectConfig.pageType == 'more') {
        state.allList = [...state.allList, ...res.data];
      } else {
        state.allList = res.data;
      }
      state.list = state.allList.map(item => {
        let cover = '';
        if (item.cover) cover = import.meta.env.VITE_CURRENT_ENV == 'dev' ? import.meta.env.VITE_DEV_BASE_SERVER + item.cover : store.projectConfig.bsServerUrl + item.cover;
        return {
          loading: false,
          id: item.id,
          title: item.title,
          artist: item.artist,
          type: item.type,
          img: cover,
          src: import.meta.env.VITE_CURRENT_ENV == 'dev' ? import.meta.env.VITE_DEV_BASE_SERVER + item.src : store.projectConfig.bsServerUrl + item.src
        };
      });
      state.page.total = res.total;
      if (res.data.length === 0 || state.list.length >= res.total) {
        state.isMore = false; // 已经没有更多数据了
      }

      scorllTopCallback && scorllTopCallback();
      if (!state.currentMusicId) state.currentMusicId = state.list[0].id;
      state.loading = false;

      // 如果本地音乐列表和服务器音乐列表长度不同，则更新本地音乐列表
      if (store.musicList.length != state.page.total) {
        store.GET_MUSIC_LIST();
      }
    })
    .finally(() => {
      state.loading = false;
    });
};
getMusicListFn();

// 加在更多
const handleLoadMore = () => {
  state.loading = true;
  state.page.page++;
  getMusicListFn();
};

// 分页改变
const handlePageChange = (page: number, size: number, scorllTopCallback: Function) => {
  state.page.page = page;
  state.page.size = size;
  getMusicListFn(scorllTopCallback);
};

const handlePlay = (item: any) => {
  state.currentMusicId = item.id;
  $bus.emit('playMusic', { id: item.id });
};

// 下载音乐
const hendleDownload = (item: any, index: number) => {
  state.list[index].loading = true;
  const x = new XMLHttpRequest();
  x.open('GET', item.src, true);
  x.responseType = 'blob';
  x.onload = function () {
    const url = window.URL.createObjectURL(x.response);
    const elink = document.createElement('a');
    elink.href = url;
    elink.target = '_self'; // 当前也 target打开新页面
    elink.setAttribute('download', item.artist + '-' + item.title + '.mp3');
    elink.style.display = 'none';
    document.body.appendChild(elink);
    elink.click();
    document.body.removeChild(elink);
    state.list[index].loading = false;
  };
  x.send();
};

// 监听滚动到当前音乐位置
const getMusicById = () => {
  const id = route.query.id;
  if (id) {
    const musicEle = document.getElementById(`music-${id}`);
    if (musicEle) {
      musicEle.scrollIntoView({ behavior: 'auto', block: 'center' });
    }
  }
};

onMounted(() => {
  // 监听 播放器 播放状态变化
  $bus.on('musicPlayerStatusChange', (n: boolean) => {
    state.isPlaying = n;
  });
  // 监听 播放器 当前音乐变化
  $bus.on('musicPlayerCurrentMusicChange', (id: number) => {
    state.currentMusicId = id;
  });
});

onUpdated(() => {
  if (state.updateTimer == 0) {
    state.updateTimer++;
    getMusicById();
  }
});

onUnmounted(() => {
  $bus.off('musicPlayerStatusChange');
  $bus.off('musicPlayerCurrentMusicChange');
});
</script>

<style lang="scss" scoped>
.music-warp {
  width: 100%;
  height: 100%;
  .music-list-content-warp {
    background-color: var(--bg-image-warp-color);
    .music-list-content {
      max-width: var(--content-max-width);
      margin: 0 auto;
      .music-list {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-between;
        padding: 20px 0 0 0;
        .music-item {
          width: 440px;
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: 10px 20px;
          box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.1);
          margin: 10px 0;
          border-radius: var(--border-radius-2);
          background-color: var(--bg-content-color);
          &:hover {
            .download-btn {
              display: block !important;
            }
            .cover-img {
              transform: scale(1.1);
            }
          }
          .cover-img {
            transition: all 0.4s ease-in-out;
          }
          .music-item-warp {
            display: flex;
            align-items: center;
          }
          .is-playing-current {
            top: 0;
            right: 20px;
            display: flex;
            align-items: center;
            .current-music {
              font-size: 24px;
              margin: 0 10px;
            }
            img {
              width: 50px;
              height: 50px;
              border-radius: 50%;
            }
          }
          .cover-warp {
            width: 100px;
            height: 100px;
            padding: 10px;
            img {
              width: 100%;
              height: 100%;
              object-fit: cover;
            }
          }
          .music-info {
            margin-left: 20px;
            color: var(--text-color);
            .music-artist,
            .music-type {
              font-size: 12px;
              margin-top: 5px;
            }
            .music-title {
              margin-bottom: 15px;
            }
          }
        }
        .active {
          border: 2px solid var(--theme-color);
          box-shadow: 2px 2px 10px 2px var(--theme-light-color-2) !important;
        }
      }
    }
    .no-article {
      max-width: var(--content-max-width);
      margin: 0 auto;
      height: calc(100vh - 400px);
    }
    .m-no-article {
      max-width: var(--content-max-width-m);
      margin: 0 auto;
      height: calc(100vh - 300px) !important;
    }
    .m-music-list-content {
      width: var(--content-max-width-m) !important;
      .music-item {
        width: 100% !important;
      }
    }
  }
  .m-music-list-content-warp {
    min-height: calc(100vh - 300px) !important;
  }
}
.bottom-loading {
  width: 100%;
  display: flex;
  justify-content: center;
  padding-bottom: 20px;
  .no-more {
    color: var(--theme-light-color-3);
  }
}
</style>
